package cc.shacocloud.mirage.restful;

import cc.shacocloud.mirage.restful.exception.HttpMediaTypeNotAcceptableException;
import cc.shacocloud.mirage.restful.exception.HttpMessageNotWritableException;
import cc.shacocloud.mirage.restful.http.GenericHttpMessageConverter;
import cc.shacocloud.mirage.restful.http.HttpMessageConverter;
import cc.shacocloud.mirage.restful.http.MediaType;
import cc.shacocloud.mirage.utils.GenericTypeResolver;
import cc.shacocloud.mirage.utils.LogFormatUtils;
import cc.shacocloud.mirage.utils.MethodParameter;
import cc.shacocloud.mirage.utils.collection.CollUtil;
import io.vertx.core.Future;
import io.vertx.core.buffer.Buffer;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Type;
import java.util.*;

/**
 * 拓展 {@link AbstractMessageConverterMethodArgumentResolver}
 * 通过使用 {@link HttpMessageConverter HttpMessageConverters} 写入响应来处理方法返回值的能力
 */
@Slf4j
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
        implements HandleMethodReturnValueHandler {
    
    private static final List<MediaType> ALL_APPLICATION_MEDIA_TYPES = Arrays.asList(MediaType.ALL, new MediaType("application"));
    
    protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> messageConverters) {
        super(messageConverters);
    }
    
    /**
     * 将给定的返回类型写入给定的输出消息
     *
     * @param value      要写入输出消息的值
     * @param returnType 值的类型
     * @param response   响应
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    protected <T> Future<Buffer> writeWithMessageConverters(@Nullable T value,
                                                            MethodParameter returnType,
                                                            HttpResponse response) {
        Object body;
        Class<?> valueType;
        Type targetType;
        
        if (value instanceof CharSequence) {
            body = value.toString();
            valueType = String.class;
            targetType = String.class;
        } else {
            body = value;
            valueType = getReturnValueType(body, returnType);
            targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
        }
        
        // --------- 匹配处理的媒体类型
        MediaType selectedMediaType = null;
        MediaType contentType = response.headers().getContentType();
        boolean isContentTypePreset = contentType != null && contentType.isConcrete();
        if (isContentTypePreset) {
            if (log.isDebugEnabled()) {
                log.debug("从响应中找到预设的 'Content-Type' :" + contentType);
            }
            selectedMediaType = contentType;
        } else {
            List<MediaType> acceptableTypes = response.request().headers().getAcceptableMediaTypes();
            List<MediaType> producibleTypes = getProducibleMediaTypes(valueType, targetType);
            
            if (body != null && producibleTypes.isEmpty()) {
                return Future.failedFuture(new HttpMessageNotWritableException("未找到类型返回值的转换器: " + valueType));
            }
            
            List<MediaType> mediaTypesToUse = new ArrayList<>();
            for (MediaType requestedType : acceptableTypes) {
                for (MediaType producibleType : producibleTypes) {
                    if (requestedType.isCompatibleWith(producibleType)) {
                        mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
                    }
                }
            }
            
            if (mediaTypesToUse.isEmpty()) {
                if (body != null) {
                    return Future.failedFuture(new HttpMediaTypeNotAcceptableException(producibleTypes));
                }
                
                if (log.isDebugEnabled()) {
                    log.debug("不支持处理可接受类型 " + acceptableTypes + " ，支持的类型： " + producibleTypes);
                }
                return Future.succeededFuture();
            }
            
            MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
            
            for (MediaType mediaType : mediaTypesToUse) {
                if (mediaType.isConcrete()) {
                    selectedMediaType = mediaType;
                    break;
                } else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
                    selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
                    break;
                }
            }
            
            if (log.isDebugEnabled()) {
                log.debug("'从给定的可接受类型 " + acceptableTypes + " 和支持的类型 " + producibleTypes + " 中寻找出并使用类型 '" + selectedMediaType + "'");
            }
        }
        
        // --------- 循环转换器处理
        if (selectedMediaType != null) {
            selectedMediaType = selectedMediaType.removeQualityValue();
            for (HttpMessageConverter<?> converter : this.messageConverters) {
                
                GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
                        (GenericHttpMessageConverter<?>) converter : null);
                
                if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
                        converter.canWrite(valueType, selectedMediaType)) {
                    
                    if (log.isDebugEnabled()) {
                        log.debug("匹配到结果类型解析处理类：" + (genericConverter != null ? genericConverter : converter));
                    }
                    
                    if (body != null) {
                        
                        if (log.isDebugEnabled()) {
                            log.debug("待处理的响应结果： [" + LogFormatUtils.formatValue(body, false) + "]");
                        }
                        
                        if (genericConverter != null) {
                            return genericConverter.write(body, targetType, selectedMediaType, response);
                        } else {
                            return ((HttpMessageConverter) converter).write(body, selectedMediaType, response);
                        }
                    } else {
                        if (log.isDebugEnabled()) {
                            log.debug("待处理的响应结果： null");
                        }
                    }
                    return Future.succeededFuture();
                }
            }
        }
        
        if (body != null) {
            Set<MediaType> producibleMediaTypes = response.context().get(VertxInvokeHandler.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
            if (isContentTypePreset || CollUtil.isNotEmpty(producibleMediaTypes)) {
                return Future.failedFuture(new HttpMessageNotWritableException("未匹配到 [" + valueType + "] 转换器来处理带有预设的Content-Type：'" + contentType + "'"));
            }
            return Future.failedFuture(new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes));
        }
        return Future.succeededFuture();
    }
    
    /**
     * 使用前者的q值返回更具体的可接受和可生产的媒体类型
     */
    private MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produceType) {
        MediaType produceTypeToUse = produceType.copyQualityValue(acceptType);
        return (MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceTypeToUse) <= 0 ? acceptType : produceTypeToUse);
    }
    
    /**
     * 返回可以产生的媒体类型。产生的媒体类型为:
     *
     * <ul>
     *   <li>可以写入特定返回值的已配置转换器的媒体类型{@link GenericHttpMessageConverter#getSupportedMediaTypes}
     *   <li>{@link MediaType#ALL}
     * </ul>
     */
    protected List<MediaType> getProducibleMediaTypes(Class<?> valueClass, @Nullable Type targetType) {
        if (!this.allSupportedMediaTypes.isEmpty()) {
            List<MediaType> result = new ArrayList<>();
            for (HttpMessageConverter<?> converter : this.messageConverters) {
                if (converter instanceof GenericHttpMessageConverter && targetType != null) {
                    if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {
                        result.addAll(converter.getSupportedMediaTypes());
                    }
                } else if (converter.canWrite(valueClass, null)) {
                    result.addAll(converter.getSupportedMediaTypes());
                }
            }
            return result;
        } else {
            return Collections.singletonList(MediaType.ALL);
        }
    }
    
    /**
     * 返回 {@code returnType} 的通用类型
     */
    private Type getGenericType(@NotNull MethodParameter returnType) {
        return returnType.getGenericParameterType();
    }
    
    /**
     * 返回要写入响应的值的类型。通常，这是通过 {@link Class#getClass} 对该值进行的简单检查，
     * 但是如果该值为null，则需要检查返回类型，可能包括泛型类型确定
     */
    protected Class<?> getReturnValueType(@Nullable Object value, MethodParameter returnType) {
        return (value != null ? value.getClass() : returnType.getParameterType());
    }
    
}
